#include "Plane.h"
#include "Ray.h"
#include "ContactData.h"
#include "BBox.h"

Plane::Plane() :
m_d(0.0f)
{}

Plane::Plane(const Vec3& norm, const float& d,
             const Material& m, bool reflect,float reflectance) : Renderable(m,reflect,reflectance),
m_norm(norm),
m_d(d)
{}

Plane::Plane(const float& a, const float& b, 
             const float& c, const float& d,
             const Material& m, bool reflect,float reflectance) : Renderable(m,reflect,reflectance),
m_norm(a,b,c),
m_d(d)
{}

Plane::Plane(const Vec3& v1, const Vec3& v2, 
             const Vec3& v3,const Material& m, bool reflect,float reflectance) : Renderable(m,reflect,reflectance)
{
    CreateFromPoints(v1,v2,v3);
}

void Plane::CreateFromPoints(const Vec3& v1, const Vec3& v2, const Vec3& v3)
{
    Vec3 temp1, temp2;
    temp1 = v1 - v2;
    temp2 = v3 - v2;

    m_norm = temp2.CrossProduct(temp1);
    m_norm.normalise();
    m_d = -(m_norm.DotProduct(v2));
}

void Plane::SetStats(const Vec3& normal, float distance)
{
    m_norm = normal;
    m_d = distance;
}

void Plane::Normalise()
{
    if(m_norm.squareMagnitude() > 1)
        m_norm.normalise();
    m_d /= m_norm.magnitude();
}

float Plane::Distance(const Vec3& q) const
{
    return (m_d + m_norm.DotProduct(q));
}

const Vec3& Plane::GetNormal() const
{
    return m_norm;
}

const float& Plane::GetOffset() const
{
    return m_d;
}

bool Plane::Hit( const Ray& ray, float& tmin, ContactData& data ) const
{
    if(!m_box.Hit(ray))      // if it doesnt hit the box, return false
        return false;

    Vec3 pointOnPlane = m_norm * m_d;
    Vec3 pointToOrigin = pointOnPlane - ray.m_origin;
    float directionDotNormal = ray.m_dir.DotProduct(m_norm);
    Vec3 normalOverDot =  m_norm / directionDotNormal;
    float t = pointToOrigin.DotProduct(normalOverDot);
    if (t > 0.001f) 
    {
        tmin = t;
        ray.m_depth = tmin;
        data.m_hitNormal = m_norm;
        data.m_hitPoint = ray.m_origin + (ray.m_dir * t);
        data.m_hitNormal.normalise();
        data.m_hitTarget = (Renderable*)this;
        return (true);	
    }

    return(false);
}
